import pyaudio
import numpy as np
import struct
import io
import sys
import os
import ctypes

# 语音处理模块

# 全局变量初始化为None
opuslib = None
OPUS_AVAILABLE = False

# 初始化Opus库
def initialize_opus():
    """初始化Opus库，包括DLL加载和库导入"""
    global opuslib, OPUS_AVAILABLE
    
    if OPUS_AVAILABLE:
        return True
    
    try:
        # Windows平台需要先加载opus.dll
        if sys.platform == 'win32':
            # 获取opus.dll的绝对路径
            current_dir = os.path.dirname(os.path.abspath(__file__))
            project_root = os.path.dirname(current_dir)
            opus_path = os.path.join(project_root, "xxx xiaozhi-web-client-main", "libs", "windows")
            
            print(f"[OPUS] 正在查找 opus.dll: {opus_path}")
            
            if os.path.exists(opus_path):
                # 将opus.dll所在目录添加到PATH环境变量
                os.environ['PATH'] = opus_path + ";" + os.environ['PATH']
                print(f"[OPUS] 已将 opus.dll 目录添加到 PATH")
                
                # 直接加载opus.dll
                opus_dll_path = os.path.join(opus_path, "opus.dll")
                if os.path.exists(opus_dll_path):
                    print(f"[OPUS] 确认 opus.dll 文件存在: {opus_dll_path}")
                    try:
                        # 预加载opus.dll
                        opus_lib = ctypes.cdll.LoadLibrary(opus_dll_path)
                        print(f"[OK] 成功预加载 opus.dll")
                    except Exception as e:
                        print(f"[ERROR] 预加载 opus.dll 失败: {e}")
                        return False
                else:
                    print(f"[ERROR] opus.dll 文件不存在: {opus_dll_path}")
                    return False
            else:
                print(f"[ERROR] opus.dll 目录不存在: {opus_path}")
                return False
        
        # 尝试导入opuslib
        try:
            import opuslib as imported_opuslib
            opuslib = imported_opuslib
            OPUS_AVAILABLE = True
            print(f"[OK] 成功导入 opuslib")
            # 输出版本信息
            print(f"[OPUS] opuslib 版本: {getattr(opuslib, '__version__', 'unknown')}")
            return True
        except ImportError as e:
            print(f"[ERROR] 导入 opuslib 失败: {e}")
            print("[HINT] 请安装 opuslib: pip install opuslib==3.0.1")
            return False
            
    except Exception as e:
        print(f"[ERROR] 初始化 Opus 库时出错: {e}")
        import traceback
        traceback.print_exc()
        return False

# 检查Opus库是否可用
def get_opus_available():
    global OPUS_AVAILABLE
    if not OPUS_AVAILABLE:
        initialize_opus()
    return OPUS_AVAILABLE

class AudioProcessor:
    def __init__(self, buffer_size=960):
        self.buffer_size = buffer_size
        self.buffer = bytearray()
    
    # 添加音频数据到缓冲区
    def add_data(self, data):
        self.buffer.extend(data)
    
    # 从缓冲区获取指定大小的数据块
    def get_chunk(self, chunk_size=None):
        if chunk_size is None:
            chunk_size = self.buffer_size
        
        if len(self.buffer) < chunk_size:
            return None
        
        chunk = self.buffer[:chunk_size]
        self.buffer = self.buffer[chunk_size:]
        return bytes(chunk)
    
    # 清空缓冲区
    def clear_buffer(self):
        self.buffer = bytearray()
    
    # 获取缓冲区当前大小
    def get_buffer_size(self):
        return len(self.buffer)

# Opus音频处理函数
def process_opus_audio(opus_data):
    """处理接收到的Opus音频数据"""
    global opuslib
    
    # 验证输入数据
    if not opus_data or len(opus_data) == 0:
        print("[WARN] 收到空的Opus数据")
        return b'\x00' * 2880
    
    # 检查Opus是否可用
    if not get_opus_available() or opuslib is None:
        # print("[WARN] Opus不可用，无法解码音频")
        # 返回空白数据作为后备
        return b'\x00' * 2880  # 24kHz * 60ms * 2字节 = 2880字节
    
    try:
        # 创建解码器 - 使用24kHz以匹配服务器
        decoder = opuslib.Decoder(24000, 1)  # type: ignore
        
        # 解码Opus数据
        frame_size = 1440  # 60ms at 24kHz
        pcm_data = decoder.decode(opus_data, frame_size, False)
        
        # 验证输出数据
        if not pcm_data or len(pcm_data) == 0:
            print("[WARN] Opus解码输出为空")
            return b'\x00' * 2880
        
        # 检查数据长度是否正确
        expected_size = 2880  # 1440 samples * 2 bytes
        if len(pcm_data) != expected_size:
            # print(f"[WARN] 解码数据长度不匹配: {len(pcm_data)} vs {expected_size}")
            # 调整数据长度
            if len(pcm_data) < expected_size:
                # 填充
                pcm_data = pcm_data + b'\x00' * (expected_size - len(pcm_data))
            else:
                # 截断
                pcm_data = pcm_data[:expected_size]
        
        return pcm_data
    except Exception as e:
        print(f"[ERROR] Opus解码失败: {e}")
        # 返回空白数据作为后备
        return b'\x00' * 2880

# PCM转Opus编码
def pcm_to_opus(pcm_data, sample_rate=16000, channels=1):
    """将PCM音频数据转换为Opus格式"""
    global opuslib
    
    # 检查Opus是否可用
    if not get_opus_available() or opuslib is None:
        print("[WARN] Opus库不可用，无法进行编码")
        # 返回原始数据的副本作为后备
        if isinstance(pcm_data, (bytes, bytearray)):
            return bytes(pcm_data)
        else:
            return bytes(str(pcm_data), 'utf-8')
    
    try:
        # 创建Opus编码器
        encoder = opuslib.Encoder(sample_rate, channels, opuslib.APPLICATION_AUDIO)  # type: ignore
        
        # 确保输入是字节数据
        if isinstance(pcm_data, list) or isinstance(pcm_data, np.ndarray):
            # 如果是列表或numpy数组，转换为bytes
            pcm_bytes = struct.pack(f'<{len(pcm_data)}h', *[int(x) for x in pcm_data])
        elif isinstance(pcm_data, bytes):
            pcm_bytes = pcm_data
        else:
            # 其他类型转换为字符串后再转bytes
            pcm_bytes = bytes(str(pcm_data), 'utf-8')
        
        # 编码PCM数据
        frame_size = 960  # 60ms at 16kHz
        encoded_data = encoder.encode(pcm_bytes, frame_size)
        return encoded_data
    except Exception as e:
        print(f"[ERROR] PCM转Opus失败: {e}")
        # 出错时返回原始数据的bytes形式作为后备
        if isinstance(pcm_data, (bytes, bytearray)):
            return bytes(pcm_data)
        else:
            return bytes(str(pcm_data), 'utf-8')

# 初始化检查
initialize_opus()